<?php

/**
 * @copyright since 8:27 23/4/2020
 * @author <mc@dancis.info>
 */

const TAB_NAME = 'demo_user';

//region Connection

const PREFIX_PERSISTENT = 'p:';

// Configuration for demonstration, programmer could make modification on your own
const DB_HOST = '127.0.0.1'; // Specific for TCP connection rather than socket with `'localhost'` or `null`
//const MYSQL_PORT = 3306;
const DB_USER = 'root';
const DB_USER_PASS = 'root'; // Warning! It just for example. Password should not be marked in the source code but environment variable.
const DB_NAME = 'dbg_gdnfu';

/**
 * Traditional usage.
 *
 * Please *MUST* handle this usage first.
 * @return mysqli
 */
function db_procedural_conn_mode1()
{
    $db = mysqli_connect(DB_HOST, DB_USER, DB_USER_PASS, DB_NAME);

    if (!$db) {
        throw new RuntimeException('Connection could not be established');
    }

    return $db;
}

/**
 * With options.
 * @return mysqli
 * @throws Exception
 */
function db_procedural_conn_mode2()
{
    if (!($db = mysqli_init())) {
        throw new Exception('Init failed');
    }
    if (!mysqli_options($db, MYSQLI_OPT_INT_AND_FLOAT_NATIVE, 1)) {
        throw new Exception('Set option failed');
    }

    if (!mysqli_real_connect(
        $db,
        DB_HOST,
        DB_USER,
        DB_USER_PASS,
        DB_NAME,
        null,
        null,
        MYSQLI_CLIENT_COMPRESS
    )) {
        throw new Exception('Connection error: #' . mysqli_connect_errno() . ': ' . mysqli_connect_error());
    }

    return $db;
}

function db_procedural_conn($mode = 1)
{
    if (!in_array($mode, range(1, 2))) {
        throw new Exception;
    }
    $func_name = 'db_procedural_conn_mode' . $mode;

    $db = $func_name();
    mysqli_set_charset($db, 'utf8mb4');
    return $db;
}

//endregion

//region CURD

function db_procedural_create()
{
    $rand = mt_rand();
    list($name, $passWd) = [implode('-', ['user', $rand]), password_hash('', PASSWORD_BCRYPT)]; // Hardcoded


    $db = db_procedural_conn();
    if (!($stmt = mysqli_prepare($db, 'INSERT INTO `' . TAB_NAME . '` (`name`, `passwd`) VALUES (?, ?)'))) {
        throw new Exception('Prepare failed: #' . mysqli_errno($db) . ' - ' . mysqli_error($db));
    }
    if (!mysqli_stmt_bind_param($stmt, 'ss', $name, $passWd)) {
        throw new Exception('Bind param failed: #' . mysqli_stmt_errno($stmt) . ' - ' . mysqli_stmt_error($stmt));
    }

    echo 'Now writing data into database table named ', TAB_NAME, PHP_EOL;
    if (mysqli_stmt_execute($stmt)) {
        return mysqli_stmt_affected_rows($stmt);
    } else {
        throw new Exception('Execution failed: #' . mysqli_stmt_errno($stmt) . ' - ' . mysqli_stmt_error($stmt));
    }
}

/**
 * Reset password as empty on the users who's name with specific string.
 * @param string $name
 * @return int|string
 * @throws Exception
 */
function db_procedural_update($name = 'name')
{
    // Init. data
    list($name_like, $passWd) = ['%' . $name . '%', password_hash('', PASSWORD_BCRYPT)];


    $db = db_procedural_conn();
    if (!($stmt = mysqli_prepare($db, 'UPDATE `' . TAB_NAME . '` SET `passwd` = ? WHERE `name` LIKE ?'))) {
        throw new Exception('Prepare failed: #' . mysqli_errno($db) . ' - ' . mysqli_error($db));
    }
    if (!mysqli_stmt_bind_param($stmt, 'ss', $passWd, $name_like)) {
        throw new Exception('Bind param failed: #' . mysqli_stmt_errno($stmt) . ' - ' . mysqli_stmt_error($stmt));
    }

    echo 'Now changing password for selected users', PHP_EOL;
    if (mysqli_stmt_execute($stmt)) {
        return mysqli_stmt_affected_rows($stmt);
    } else {
        throw new Exception('Execution failed: #' . mysqli_stmt_errno($stmt) . ' - ' . mysqli_stmt_error($stmt));
    }
}

function db_procedural_read_pass_wd($id)
{
    $db = db_procedural_conn();
    if (!($stmt = mysqli_prepare($db, 'SELECT `passwd` FROM `' . TAB_NAME . '` WHERE `id` = ?'))) {
        throw new Exception('Prepare failed: #' . mysqli_errno($db) . ' - ' . mysqli_error($db));
    }
    if (!mysqli_stmt_bind_param($stmt, 'i', $id)) {
        throw new Exception('Bind param failed: #' . mysqli_stmt_errno($stmt) . ' - ' . mysqli_stmt_error($stmt));
    }

    if (!mysqli_stmt_execute($stmt)) {
        throw new Exception('Execution failed: #' . mysqli_stmt_errno($stmt) . ' - ' . mysqli_stmt_error($stmt));
    }

    if (!mysqli_stmt_bind_result($stmt, $pass_wd)) {
        throw new Exception('Bind result param failed: #' . mysqli_stmt_errno($stmt) . ' - ' . mysqli_stmt_error($stmt));
    }
    if (mysqli_stmt_fetch($stmt) === false) {
        throw new Exception('Fetch result failed: #' . mysqli_stmt_errno($stmt) . ' - ' . mysqli_stmt_error($stmt));
    } else {
        return $pass_wd;
    }
}

function db_procedural_read($id = null, bool $just_pass_wd = false)
{
    $db = db_procedural_conn();
    if ($id === null) { // Just using `query` function
        if ($result = mysqli_query($db, 'SELECT * FROM `' . TAB_NAME . '`')) {
            $ret = [];
            foreach ($result as $tuple) {
                $ret[] = &$tuple;
            }
            return $ret;
        } else {
            throw new Exception('Query failed: #' . mysqli_errno($db) . ' - ' . mysqli_error($db));
        }
    } else {
        if ($just_pass_wd === true) {
            return db_procedural_read_pass_wd($id);
        }

        if (!($stmt = mysqli_prepare($db, 'SELECT * FROM `' . TAB_NAME . '` WHERE `id` = ?'))) {
            throw new Exception('Prepare failed: #' . mysqli_errno($db) . ' - ' . mysqli_error($db));
        }
        if (!mysqli_stmt_bind_param($stmt, 'i', $id)) {
            throw new Exception('Bind param failed: #' . mysqli_stmt_errno($stmt) . ' - ' . mysqli_stmt_error($stmt));
        }

        echo 'Now fetching the table named ', TAB_NAME, PHP_EOL;
        if (!mysqli_stmt_execute($stmt)) {
            throw new Exception('Execution failed: #' . mysqli_stmt_errno($stmt) . ' - ' . mysqli_stmt_error($stmt));
        }

        // Standard operation
        //if (($meta = mysqli_stmt_result_metadata($stmt)) === false) {
        //    throw new Exception('Getting meta data failed: #' . mysqli_stmt_errno($stmt) . ' - ' . mysqli_stmt_error($stmt));
        //}
        //
        //if ($meta_result = mysqli_fetch_fields($meta)) {
        //    $field_list_name = [];
        //    foreach ($meta_result as &$field_info) {
        //        $field_list_name[] = $field_info->name;
        //    }
        //
        //    $param_list = [];
        //    $tuple = array_fill(0, mysqli_num_fields($meta), null);
        //    foreach ($tuple as &$field_content) {
        //        $param_list[] = &$field_content;
        //    }
        //    call_user_func_array('mysqli_stmt_bind_result', array_merge([$stmt], $param_list));
        //    if (mysqli_stmt_fetch($stmt) === false) {
        //        throw new Exception('Fetching tuple data failed: #' . mysqli_stmt_errno($stmt) . ' - ' . mysqli_stmt_error($stmt));
        //    } else {
        //        return [
        //            'field_list_name' => &$field_list_name,
        //            'tuple' => &$tuple,
        //        ];
        //    }
        //} else {
        //    throw new Exception('Fetching meta data failed: #' . mysqli_stmt_errno($stmt) . ' - ' . mysqli_stmt_error($stmt));
        //}

        // An easy way to achieve
        if ($result = mysqli_stmt_get_result($stmt)) {
            $ret = [];
            foreach ($result as $tuple) {
                $ret[] = &$tuple;
            }
            return $ret;
        } else {
            throw new Exception('Getting result failed: #' . mysqli_stmt_errno($stmt) . ' - ' . mysqli_stmt_error($stmt));
        }
    }
}

function db_procedural_delete($id)
{
    $db = db_procedural_conn();
    if (!($stmt = mysqli_prepare($db, 'DELETE FROM `' . TAB_NAME . '` WHERE `id` = ?'))) {
        throw new Exception('Prepare failed: #' . mysqli_errno($db) . ' - ' . mysqli_error($db));
    }
    if (!mysqli_stmt_bind_param($stmt, 'i', $id)) {
        throw new Exception('Bind param failed: #' . mysqli_stmt_errno($stmt) . ' - ' . mysqli_stmt_error($stmt));
    }

    if (!mysqli_stmt_execute($stmt)) {
        throw new Exception('Execution failed: #' . mysqli_stmt_errno($stmt) . ' - ' . mysqli_stmt_error($stmt));
    } else {
        return mysqli_stmt_affected_rows($stmt);
    }
}

//endregion

function verify_user_pass_wd_unencrypted($user_id, $password_input)
{
    return $password_input === db_procedural_read_pass_wd($user_id);
}

function verify_user_pass_wd_encrypted($user_id, $password_input)
{
    return password_verify($password_input, db_procedural_read_pass_wd($user_id));
}

function run()
{
    //print_r(db_procedural_conn(2)->host_info); // Verify connection


    //print_r(db_procedural_create()); // Creation
    //print_r(db_procedural_update()); // Updating

    // Reading
    print_r(db_procedural_read());
    //print_r(db_procedural_read(1));
    //print_r(db_procedural_read(1, true));
    //print_r(db_procedural_read_pass_wd(1));

    //print_r(db_procedural_delete(669)); // Deleting


    //var_dump(verify_user_pass_wd_unencrypted(1, ''));
    //var_dump(verify_user_pass_wd_encrypted(1, ''));
}

// Entrance
(function () {
    try {
        run();
    } catch (Exception $exception) {
        exit($exception);
    }
})();
